package com.ftqh.tutorial.security.oauth2.provider.client;

import com.ftqh.tutorial.security.oauth2.provider.client.enums.GrantType;
import com.ftqh.tutorial.security.oauth2.provider.client.enums.RefreshTokenValidity;
import com.ftqh.tutorial.security.oauth2.provider.client.enums.RefreshTokenValidityConverter;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import java.util.*;

/**
 * \* Created with IntelliJ IDEA.
 * \* User: Administrator
 * \* Date: 2017/1/5
 * \* Time: 15:33
 * \* To change this template use File | Settings | File Templates.
 * \* Description:
 * \
 */
@Entity
@Table(name="sys_security_oauth_client_details")
@EntityListeners(AuditingEntityListener.class)
@Data
@Builder
@org.codehaus.jackson.map.annotate.JsonSerialize(include = org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_DEFAULT)
@org.codehaus.jackson.annotate.JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_DEFAULT)
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
public class OAuth2ClientDetails implements ClientDetails {
    private static final long serialVersionUID = 2474911229696850298L;
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(
            name = "uuid",
            strategy = "org.hibernate.id.UUIDGenerator",
            parameters = {
                    @Parameter(
                            name = "uuid_gen_strategy_class",
                            value = "org.hibernate.id.uuid.CustomVersionOneStrategy"
                    )
            }
    )
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    private String clientId;
    private String clientSecret;
    /**
     * custom field
     */
    private String clientName;
    private String scope = "openid";
    private String resourceIds;
    private String authorizedGrantTypes;
    private String registeredRedirectUris;
    private String autoApprove;
    private String authorities;
    private Integer accessTokenValiditySeconds = 60 * 60 * 2;  //2小时
    @Convert( converter = RefreshTokenValidityConverter.class )
    private RefreshTokenValidity refreshTokenValiditySeconds = RefreshTokenValidity.SEVEN_DAY;
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @Column(name = "additional_information")
    private String additionalInfos;
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @Transient
    private Map<String, Object> additionalInformation = new LinkedHashMap<>();
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @CreatedDate
    private Long createdAt;
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @LastModifiedDate
    private Long lastModified;



    @Override
    public String getClientId() {
        return clientId;
    }

    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @Override
    public Set<String> getResourceIds() {
        if(StringUtils.hasText(resourceIds)) {
            return StringUtils.commaDelimitedListToSet(resourceIds);
        }
        return Collections.emptySet();
    }

    @Override
    public boolean isSecretRequired() {
        return clientSecret != null;
    }

    @Override
    public String getClientSecret() {
        return clientSecret;
    }

    @Override
    public boolean isScoped() {
        return scope != null && !scope.isEmpty();
    }

    @Override
    public Set<String> getScope() {
        if (StringUtils.hasText(scope)) {
            return StringUtils.commaDelimitedListToSet(scope);
        }
        return Collections.emptySet();
    }


    @Override
    public Set<String> getAuthorizedGrantTypes() {
        if(StringUtils.hasText(authorizedGrantTypes)) {
            return StringUtils.commaDelimitedListToSet(authorizedGrantTypes);
        } else {
            return new HashSet<>(Arrays.asList(GrantType.authorization_code.getValue(), GrantType.refresh_token.getValue()));
        }
    }

    @Override
    public Set<String> getRegisteredRedirectUri() {
        if(StringUtils.hasText(registeredRedirectUris)) {
            return StringUtils.commaDelimitedListToSet(registeredRedirectUris);
        }
        return Collections.emptySet();
    }

    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        if (StringUtils.hasText(authorities)) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(authorities);
        }
        return Collections.emptyList();
    }


    @Override
    public Integer getAccessTokenValiditySeconds() {
        return accessTokenValiditySeconds;
    }

    @Override
    public Integer getRefreshTokenValiditySeconds() {
        return refreshTokenValiditySeconds!=null?refreshTokenValiditySeconds.getSeconds():null;
        //return refreshTokenValiditySeconds.getSeconds();
    }

    @Override
    public boolean isAutoApprove(String scope) {
        if(this.autoApprove == null) {
            return false;
        } else {
            Set<String> autoApproveScopes = this.getAutoApproveScopes();
            Iterator iterator = autoApproveScopes.iterator();

            String auto;
            do {
                if(!iterator.hasNext()) {
                    return false;
                }
                auto = (String)iterator.next();
            } while(!auto.equals("true") && !scope.matches(auto));

            return true;
        }
    }

    @Override
    public Map<String, Object> getAdditionalInformation() {
        return Collections.unmodifiableMap(additionalInformation==null?new LinkedHashMap<>():additionalInformation);
    }



//    @org.codehaus.jackson.annotate.JsonProperty("authorities")
//    @com.fasterxml.jackson.annotation.JsonProperty("authorities")
//    private List<String> getAuthoritiesAsStrings() {
//        return new ArrayList<>(AuthorityUtils.authorityListToSet(this.getAuthorities()));
//    }
//    @com.fasterxml.jackson.annotation.JsonProperty("authorities")
//    @com.fasterxml.jackson.databind.annotation.JsonDeserialize(
//            using = Jackson2ArrayOrStringDeserializer.class
//    )
//    private void setAuthoritiesAsStrings(Set<String> values) {
//        this.setAuthorities(String.join(",", (String[])values.toArray(new String[values.size()])));
//    }
    @org.codehaus.jackson.annotate.JsonIgnore
    @com.fasterxml.jackson.annotation.JsonIgnore
    public Set<String> getAutoApproveScopes() {
        if(StringUtils.hasText(autoApprove)) {
            return StringUtils.commaDelimitedListToSet(autoApprove);
        }
        return Collections.emptySet();
    }

    @Tolerate
    OAuth2ClientDetails() {}
}
